
#include <stdint.h>
#include <stdio.h>
#include <string.h>
#include <stdbool.h>
#include <ctype.h>

#include "soc.h"
#include "fatfs.h"






#define SDIF_TRANS_INT_DIS			0x100
#define SDIF_TRANS_ERR_CHK_EN		0x080			
#define SDIF_TRANS_MULTI			0x020
#define SDIF_TRANS_READ				0x010
#define SDIF_TRANS_BLK_CNT_EN		0x002
#define SDIF_TRANS_DMA_EN			0x001

#define SDIF_CMD_DATA               0x020
#define SDIF_CMD_IDX_CHECK          0x010
#define SDIF_CMD_CRC_CHECK          0x008

#define SDIF_CMD_RESP_136          	0x001
#define SDIF_CMD_RESP_48          	0x002
#define SDIF_CMD_RESP_48_BUSY      	0x003

#define SDIF_INT_XFER_COMPLETE		0x001
#define SDIF_INT_BUF_RD_READY 		0x020
#define SDIF_INT_ERROR 				0x8000


extern int global_variable; // 声明外部全局变量

/*
CMD17, read single block 
*/

int sdcard_read_single( uint32_t sidx, void * pary)
{
	uint32_t temp;
	uint32_t resp;
	cvi_sdhc_regs_t * psdhc = (cvi_sdhc_regs_t *)0x4310000;
	uint32_t * pu32;

	/**/
	psdhc->BlkSize = 0x200;
	psdhc->Argument = sidx*512;
	psdhc->ClockCtrl = 0x010F; //时钟频率 
	// printf( "cc : %04x\n", cc ); // 默认值0X030F
	psdhc->TransMode = SDIF_TRANS_ERR_CHK_EN | SDIF_TRANS_READ;
	psdhc->Command = (17 << 8) | SDIF_CMD_DATA | SDIF_CMD_IDX_CHECK | SDIF_CMD_CRC_CHECK | SDIF_CMD_RESP_48;
	/* wait .. */
	while ( true )  {

		temp = psdhc->NormalIntStatus;
		if ( temp & SDIF_INT_XFER_COMPLETE )  {
			psdhc->NormalIntStatus = SDIF_INT_XFER_COMPLETE;
			break;
		}

		if ( temp & SDIF_INT_ERROR )  {
			printf( "error1 : %08lx\n", temp );
			return 22;
		}

	}

	// todo : check response.
	resp = psdhc->Response[0];
	// printf( "resp : %08x\n", resp );

	/* wait .. */
	while ( true )  {
		temp = psdhc->NormalIntStatus;
		if ( temp & SDIF_INT_BUF_RD_READY )  {
			break;
		}
	}

	/**/
	pu32 = (uint32_t *)pary;
	for ( int i=0; i<128; i++ )  {
		pu32[i] = psdhc->BuffPort;
		// printf( "buff[%d] : %08x\n",i, pu32[i] );
	}

	/**/
	psdhc->NormalIntStatus = SDIF_INT_BUF_RD_READY;
	return 0;
}

/*
CMD12
*/
int sdcard_stop(void )
{
	uint32_t temp;
	uint32_t resp;
	cvi_sdhc_regs_t * psdhc = (cvi_sdhc_regs_t *)0x4310000;
	uint32_t * pu32;

	/**/
	psdhc->BlkSize = 0x200;
	psdhc->Argument = 0;
	psdhc->TransMode = SDIF_TRANS_ERR_CHK_EN | SDIF_TRANS_READ;
	psdhc->Command = (12 << 8) | SDIF_CMD_DATA | SDIF_CMD_IDX_CHECK | SDIF_CMD_CRC_CHECK | SDIF_CMD_RESP_48_BUSY;

	/* wait .. */
	while ( true )  {

		temp = psdhc->NormalIntStatus;
		if ( temp & SDIF_INT_XFER_COMPLETE )  {
			psdhc->NormalIntStatus = SDIF_INT_XFER_COMPLETE;
			break;
		}

		if ( temp & SDIF_INT_ERROR )  {
			printf( "error1 : %08lx\n", temp );
			return 22;
		}
	
		temp = psdhc->ErrorIntStatus;
		if ( temp != 0 )  {
			printf( "error2 : %08lx\n", temp );
			return 33;
		}
		
	}
	/* wait .. */
	while ( true )  {
		temp = psdhc->NormalIntStatus;
		if ( temp & SDIF_INT_BUF_RD_READY )  {
			break;
		}
	}
	/**/
	psdhc->NormalIntStatus = SDIF_INT_BUF_RD_READY;
	return 0;
}

#define __PACKED    __attribute__((packed, aligned(1)))

typedef struct _tag_part_entry  {
	uint8_t btflag;				/* boot Indicator */
	uint8_t sttchs[3];			/* start : cylinder, head, sector */
	uint8_t fstype;				/* Partition Type,  0x0B, 0x0C */
	uint8_t endchs[3];			/* End : cylinder, head, sector */
	uint32_t sttlba;			/* 开始位置, sector 单位. */
	uint32_t totals;			/* 分区容量, sector 单位. */
} part_entry_t;



/*
1. Reserved Region;  BPB 在第一个 sector.
2. FAT Region;  可以有多个 FAT 作为备份, 内容应该相同.
3. Root Directory Region; 根目录表.
4. Data Region;  文件或子目录内容区域.
*/


typedef struct __PACKED _tag_bpb_block  {
	uint8_t jmp_boot[3];  			/* 0xEB, 0x3C, 0x90 */
	uint8_t oem_name[8];			/* mkfs tools name */
	uint16_t bytes_per_sector;		/* Count of bytes per sector. */
	uint8_t sectors_per_clus;		/* Number of sectors per allocation unit. */
	uint16_t rsvd_sector_cnt;		/* Number of reserved sectors. */

	uint8_t num_fats;				/* count of file allocation tables (FATs)  */
	uint16_t root_entry_cnt;		/* root directory, entry count */
	uint16_t total_sec16;			/* zero, --> total_sec32 */
	uint8_t media_type;				/**/
	uint16_t fat_siz16;				/* fat sectors count */
	uint16_t sectors_per_track;		/* Sectors per track  for interrupt 0x13. */
	uint16_t num_of_head;			/* Number of heads for interrupt 0x13. */
	uint32_t hidden_sectors;
	uint32_t total_sec32;			/**/

	uint8_t  drive_number;			/* drive number for interrupt 0x13. */
	uint8_t  rsvd1;
	uint8_t  boot_signature;
	uint32_t vol_serialno;			/**/
	uint8_t  vol_label[11];
	uint8_t  filesys_type[8];		/**/

} bpb_block_t;



typedef struct _tag_dirent {

	uint8_t  file_name[11];		/* 8+3, */
	uint8_t  file_attr;			/* 0x10=DIRECTORY, 0x0F=LONG_NAME, 0x20=ARCHIVE */
	uint8_t  rsvd1;
	uint8_t  crt_timeten;		/* Count of tenths of a second.  */
	uint16_t  crt_time;			/* Granularity is 2 seconds */
	
	uint16_t  crt_date;			/* Creation date. */
	uint16_t  acc_date;			/* Last access date. */
	uint16_t  clus_high;		/* High word of first cluster number */
	uint16_t  wrt_time;			/* Last modification (write) time. */
	uint16_t  wrt_date;			/* Last modification (write) date. */
	uint16_t  clus_low;			/* Low word of first cluster number  */
	uint32_t  file_size;		/* size in bytes of file/directory described by this entry. */

} dirent_t;

/*
确定 分区表, 找到第一个 FAT 分区.
确定 header 格式, 目前认识 FAT16, 识别参数信息.
确定 FAT, DIR table 位置,  读入到缓冲区.

1. Master Boot Record (MBR), standard BIOS partition table.
*/
 
int fatfs_init( fatfs_t * pfatfs )
{
	int iret;
	part_entry_t  partent;
	bpb_block_t * pbpb;
	uint8_t tary[512];
	// check partion table.
	iret = sdcard_read_single( 0, tary );
	if ( iret != 0 )  {
		return 1;
	}

	// check tail signature.
	if ( (tary[510] != 0x55) || (tary[511] != 0xAA) ) {
		return 2;
	}

	//
	memcpy( (void *)&partent, &tary[446], sizeof(part_entry_t) );
	if ( partent.fstype != 0x0C ) {
		return 3;
	}

	// bpb : BIOS Parameter Block, 
	pfatfs->sttlba = partent.sttlba;
	pfatfs->totals = partent.totals;
	iret = sdcard_read_single( pfatfs->sttlba, tary);
	if ( iret != 0 )  {
		return 4;
	}

	// check tail signature.
	if ( (tary[510] != 0x55) || (tary[511] != 0xAA) ) {
		return 7;
	}

	// check, fs type.
	pbpb = (bpb_block_t *)tary;
	printf( "align? = %x\n", pbpb->bytes_per_sector );
	if ( pbpb->bytes_per_sector != 512 )  {
		return 51;
	}
	
	if ( 0 != memcmp( pbpb->filesys_type, "FAT16\x20\x20\x20", 8  ) )  {
		return 52;
	}

	if ( pfatfs->totals < pbpb->total_sec32 )  {
		return 53;
	}

	// root directory size;  (cnt * 32) / 512 = cnt / 16;
	// todo : check, 数据范围, 能否被 FAT 表管理.
	pfatfs->clusiz = pbpb->sectors_per_clus;
	pfatfs->fatofs = pbpb->rsvd_sector_cnt;
	pfatfs->fatsiz = pbpb->fat_siz16;
	pfatfs->rdrofs = pfatfs->fatofs + ( pfatfs->fatsiz * (uint32_t)pbpb->num_fats );
	pfatfs->rdrsiz = pbpb->root_entry_cnt / 16;
	pfatfs->datofs = pfatfs->rdrofs + pfatfs->rdrsiz;
	return 0;
}

/*
FAT 文件系统,  目录结构中文件名使用 8+3 格式, Short File Names. 
name 部分不足 8 个字符, 使用 0x20 补齐.
ext 部分不足 3 个字符, 也是用 0x20 补齐.
隐含分隔符 '.' = 0x2E.
并且 name, ext 其中的英文字母部分全部是大写格式.
*/
static int fname_match( uint8_t * pfn83, const char * fname )
{
	int idx;
	int ilen;
	char * ptr;
	uint8_t tname[11];

	ilen = strlen( fname );
	if ( (ilen <= 0) || (ilen > 12) )  {
		return 11;
	}

	// fill 11位 by 0x20
	memset( tname, 0x20, 11 );

	/*若没有分隔符, 则只匹配 name 部分, 若有分隔符, 则匹配 name+ext 部分.*/
	ptr = strchr( fname, 0x2E );// find '.' 返回指针（ptr指向'.'）, 若没有, 则返回 NULL.
	if ( ptr == NULL )  {

		if ( ilen > 8 )  {
			return 22;
		}

		// only name part, convert to Upper Case.
		for ( int i=0; i<ilen; i++ )  {
			tname[i] = toupper( (uint8_t)fname[i] );
		}

	} else {

		idx = ptr - fname;//ptr指向'.'的位置.fname指向文件名的开始位置.
		if ( idx > 8 )  {
			return 23;
		}

		// check ext too long ??
		if ( (ilen - idx ) > 4  )  {
			return 24;
		}

		// 
		for ( int i=0; i<idx; i++ )  {
			tname[i] = toupper( (uint8_t)fname[i] );
		}

		for ( int i=0; i< (ilen - idx); i++ )  {
			tname[8+i] = toupper( (uint8_t)fname[idx + 1 + i] );
		}
		
	}

	return memcmp( pfn83, tname, 11 );
}


static int fatfs_find_dirent( fatfs_t * pfatfs, const char * fname, dirent_t * pent )
{
	int iret;
	dirent_t * pdent;
	uint32_t rdrpos;
	uint8_t tary[512];

	// read RootDirectory sector-0.
	rdrpos = pfatfs->sttlba + pfatfs->rdrofs;
	iret = sdcard_read_single( rdrpos, tary );
	if ( iret != 0 )  {
		return 1;
	}

	// todo : search Root Directory
	pdent = (dirent_t *)tary;
	for ( int i=0; i<16; i++ )  {

		if ( pdent[i].file_attr != 0x20 )  {
			continue;
		}

		if ( 0 != fname_match( pdent[i].file_name, fname ) )  {
			continue;
		}

		// dir entry copy
		memcpy( pent, &pdent[i], sizeof(dirent_t) );
		return 0;
	}

	return 33;
}


static int fatfs_full_chain( fatfs_t * pfatfs, uint16_t clush, uint16_t * pary ) 
{
	int iret;
	int idx;
	uint32_t temp;
	uint32_t fatpos;
	uint16_t clusmax;
	uint16_t fatbl[256];


	// xxxx
	clusmax = (uint16_t)( (pfatfs->totals - pfatfs->rdrofs) / pfatfs->clusiz );// 总簇数.
	fatpos = 0;// fat初始位置.
	pary[0] = clush;// 第一个簇.
	
	printf( "first chain : %x\n", clush );
	printf( "fatsiz = %lu\n", pfatfs->fatsiz );
	printf( "clusmax = %u\n", clusmax );

	// 
	for ( idx=1; ; idx+=1 )  {

		// 
		temp = pfatfs->sttlba + pfatfs->fatofs + (clush / 256);

		if ( fatpos != temp )  {
			
			iret = sdcard_read_single( temp, fatbl );
			if ( iret != 0 )  {
				return -200;
			}

			fatpos = temp;
			printf( "fat pos : %lx\n", temp );
		}

		// get next.
		temp = clush % 256;
		clush = fatbl[temp];
		// printf( "nxt chain : %x\n", clush );

		// check end..
		if ( clush >= 0xFFF8 )  {
			break;
		}

		// 非法值.
		if ( (clush >= clusmax) || (clush < 2) )  {
			return -201;
		}

		// list 超长 ??
		if ( idx >= 30000)  {
			return -202;
		}

		// 正常, next chain.
		pary[idx] = clush;
	}

	/**/
	return idx;
}


/*
仅仅支持根目录, 不搜索下一级目录.
确定文件是否存在, 返回文件长度??...
*/
int fatfs_fstat( fatfs_t * pfatfs, const char * fname, uint32_t * psiz )
{
	int iret;
	dirent_t dent;

	iret = fatfs_find_dirent( pfatfs, fname, &dent );
	if ( iret != 0 )  {
		// not found..
		return 123;
	}

	//
	*psiz = dent.file_size;
	return 0;
}


/*
读文件的整个内容, 到指定内存位置.
返回文件长度??
*/
int fatfs_fread( fatfs_t * pfatfs, const char * fname, uint8_t * ptr )
{
	int iret;
	int iclus;
	uint32_t tsiz;
	dirent_t dent;
	uint32_t secpos;
	uint16_t chain[51200];

	iret = fatfs_find_dirent( pfatfs, fname, &dent );
	if ( iret != 0 )  {
		// not found..
		return -123;
	}

	if ( dent.clus_high != 0 )  {
		return -124;
	}

	// 
	iclus = fatfs_full_chain( pfatfs, dent.clus_low, chain );
	if ( iclus <= 0 )  {
		printf( "full chain : %d\n", iclus );
		return -125;
	}

	tsiz = 0;
	for ( int i=0; i<iclus; i++ )  {

		// printf( "chain[%d] : %u\n", i, chain[i] );

		secpos = (uint32_t)(chain[i] - 2) * pfatfs->clusiz;
		secpos = pfatfs->sttlba + pfatfs->datofs + secpos;
		// printf( "secpos : %lu\n", secpos );

		// 每个 cluster 是由若干 sector 组成.
		for ( int j=0; j< (int)pfatfs->clusiz; j++ )  {
			
			iret = sdcard_read_single( (secpos + j), ptr );
			if ( iret != 0 ) {
				return -200;
			}
			
			ptr += 512;
		}
		// total size...
		tsiz += pfatfs->clusiz * 512;
	}

	return tsiz;
}





